#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <map>
#include <set>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "messages.h"
#include "server_serialize.cpp"
using namespace std;
set<int,greater<int> > files_desc,settmp;//store all the files descriptors
fd_set read_fds;

#ifdef DEBUG
#define debug(x...) printf(x)
#else
#define debug(x...) do{}while(0)
#endif


int create_connection(int &fdmax,int port)
{
  // opening socket
  int listener = socket(AF_INET, SOCK_STREAM, 0);
  if (listener < 0) error("error opening socket");
	
  // binding
  sockaddr_in bind_addr;
  memset(&bind_addr, 0, sizeof(bind_addr));
  bind_addr.sin_family = AF_INET;
  bind_addr.sin_addr.s_addr = INADDR_ANY;
  bind_addr.sin_port = htons(port);
  if (bind(listener, (sockaddr*)&bind_addr, sizeof(bind_addr)) < 0)
    error("error binding");

  // listening to connections
  listen(listener, 125);
  // add the listener to the master set
  FD_SET(listener, &read_fds);

  // keep track of the biggest file descriptor
  files_desc.insert(listener);
  return listener;
}

map <int,string> client_list;//store all clients
void add_client(int client,char *name)
{
  debug("Adding client:%s <%d>\n",name,client);
  files_desc.insert(client);
  client_list[client]=string(name);
 
}
void remove_client(int client)
{
  debug("Removing client:%s <%d>\n",client_list[client].c_str(),client);
  files_desc.erase(client);
  client_list.erase(client);
  FD_CLR(client,&read_fds);
}
//receive msg from some client and answer it
void evaluate_client_message(int client)
{
  char buf[65555]={0};
  if(! my_read(client,buf,2))
    {remove_client(client);return;}
  int tam = msg_length(buf);
  if(!my_read(client,buf,tam))
    {remove_client(client);return;} 
  int type = deserializeLS(buf);
  MessageSLMessage msg;
  char *buf2;
  
  if(type==LS_ASSOCIATE)//new client
    {
      add_client(client,buf + 1);//add client to list
      msg.type = SL_ASSOCIATE_OK;
      buf2 = serializeSL(&msg);
      if(! my_write(client,buf2,3))
	remove_client(client);
		
    }
  else if(type==LS_DEASSOCIATE)//lost client
    {
      msg.type = SL_DEASSOCIATE_OK;
      buf2 = serializeSL(&msg);
      my_write(client,buf2,3);
      remove_client(client);//remove client from the list
    }
  else if(type==LS_MESSAGE)//message from client
    {
      msg.type = SL_MESSAGE;
      string s = client_list[client];
      strcpy(msg.message,(s+" : "+string(buf+1)).c_str());
      buf2 = serializeSL(&msg);
      for(map<int,string>::iterator it = client_list.begin();it!=client_list.end();it++)
	if(!my_write(it->first,buf2,min((int)s.size()+3+tam+2,1<<15)))
	  remove_client(client);
    }
  else
    {
      debug("Wrong client type");
    }
  
}
//receive msg from the coordinator and answer it
bool evaluate_coordinator_message(int coordinator)
{
  char buf[64]={0};
  if(!my_read(coordinator,buf,2))
    error("can't read coordinator message");
  if(!my_read(coordinator,buf,msg_length(buf)))
    error("can't read coordinator message");
  int type = deserializeCS(buf);
  MessageSC msg;
  bool kill = false;
  bool msg_ok = true;
  if(type==CS_PING)//ping...pong!
    {
      msg.type = SC_PONG;
    }
  else if(type==CS_DELETE_ROOM)//delete_room :(
    {
      if(client_list.empty())//no client,can kill
	{
	  msg.type = SC_ROOM_DELETED;
	  kill = true;	
	}
      else//can't kill
	{
	  msg.type = SC_ROOM_HAS_ASSOCIATES;
	}
    }
  else
    {
      msg_ok = false;	
      debug("Wrong coordinator type");
    }	
 
  if(msg_ok)
  {
   char *buf2 = serializeSC(&msg);
   if(!my_write(coordinator,buf2,3))
     error("can't write coordinator message");
  } 
 return kill;
}
int main(int argc, char *argv[])//receive the ports on the arguments
{

  if(argc<3)
    error("Usage: server CLIENT_PORT_NUMBER COORDINATOR_PORT_NUMBER");
#ifdef DEBUG
  setbuf(stdout,NULL);
  debug("New Server, listening clients at port: %d and coordinator at %d\n",atoi(argv[1]),atoi(argv[2]));
#endif
 
 
  
  //clearing the set
  files_desc.clear();
  FD_ZERO(&read_fds);
  int fdmax = 0;
  int client_listener = create_connection(fdmax,atoi(argv[1]));//receive new connections from clients
  fdmax =  *files_desc.begin()+1;
  int coordinator_listener = create_connection(fdmax,atoi(argv[2]));//receive new clients

  //main loop, wait for connections and messages
  while (true)
    {
   		int fdmax = *files_desc.begin()+1;
      fd_set tmp = read_fds;
	    
  if(select(fdmax,&tmp,NULL,NULL,NULL)==-1)//waits until receive some package
	{
	  error("select error");
	}
	 settmp = files_desc;	
	  for(set<int,greater<int> >::iterator it=settmp.begin();it!=settmp.end();it++)
	{
	  int listener = *it;
	  if(FD_ISSET(listener,&tmp))
	    {
	      if( listener == client_listener)//new_client
		{
		 
		  struct sockaddr_storage remoteaddr; 
		  socklen_t addrlen;
		  addrlen  = sizeof( remoteaddr);
		  int newfd = accept(client_listener,(struct sockaddr *)&remoteaddr,&addrlen);	
		  debug("server::message from client\n");
		  if(newfd>0)//succes!
		    {
		      FD_SET(newfd,&read_fds);//add the client to the read set
		      evaluate_client_message(newfd);//receive the LS_ASSOCIATE msg
		    }
		}	
	      else if(listener == coordinator_listener)//message from the coordinator
		{
		  struct sockaddr_storage remoteaddr; 
		  socklen_t addrlen;
		  addrlen  = sizeof( remoteaddr);
		  int newfd = accept(coordinator_listener,(struct sockaddr *)&remoteaddr,&addrlen);	
		  if(newfd==-1)
		    {
		       error("connection failed with coordinator");
		    }
		  else
		    {
		      debug("connection with coordinator ok\n");
		      if(evaluate_coordinator_message(newfd))
			{
			  debug("Close Server,clients at port %d and coordinator at %d\n",atoi(argv[1]),atoi(argv[2]));
			  exit(0);
			}
		      close(newfd);
		   
		    }
		  
		}
	      else //message from some client
		{
		  evaluate_client_message(listener);//receive the msg from the client
		
		}
		
	    }	
	}
	
    }
  error(" ???");
  return 1;//shouldn't get here
}
